#ifndef MODULES_CORE_P2PSERVICE_H_
#define MODULES_CORE_P2PSERVICE_H_
#include "../common.h"
using namespace std;

class AddressTool {
public:
	static String toIp(String str, String cmd, char theChar = ':');
	static int toPort(String str, char theChar = ':');
	static String toArg(String str, String cmd);
};
class UdpWarp {
private:
	AsyncUDP udp;
	IPAddress addr;
	int port;
	AsyncUDPMessage message;

public:
	UdpWarp();
	~UdpWarp() {
		close();
	}
	UdpWarp(IPAddress addr, int port) :
					addr(addr), port(port) {
		this->addr.fromString(addr.toString());
	}
	UdpWarp(String addr, int port) :
					port(port) {
		this->addr.fromString(addr);
	}
	void onPacket(AuPacketHandlerFunction cb);
	bool connected() {
		return this->udp.connected();
	}
	bool checkAddr(String addr, int port = -1) {
		if (port > 0) {
			return this->addr.toString() == addr && this->port == port;
		} else {
			return this->addr.toString() == addr;
		}
	}
	void close();
	/**
	 * clinet
	 * **/
	bool connect();
	/**
	 * server
	 * **/
	bool listen();
	bool listen(IPAddress addr, int port);
	bool listen(String addr, int port);

	bool send(std::string message, IPAddress addr, int port);
	bool send(std::string message, String addr, int port);
};

class P2PClient {
public:
	P2PClient(String sid, String addr, int port) :
					sid(sid), strAddr(addr), port(port) {
		this->addr.fromString(addr);
	}
	;
	String strAddr;
	IPAddress addr;
	int port;
	String sid;
};
static String PROXY_SERVER = "__PROXY_SERVER___";
class P2PService {

private:
	UdpWarp *proxyServer = nullptr;
	std::map<String, P2PClient*> clients;
	void close(UdpWarp *udp) {
		if (udp == nullptr) {
			return;
		}
		udp->close();
		delete udp;
	}

public:
	~P2PService() {
		close();
	}
	/**
	 * 连接代理服务器
	 * **/
	bool connectProxyServer(String addr, int port, AuPacketHandlerFunction cb) {
		close(proxyServer);
		proxyServer = new UdpWarp(addr, port);
		bool ok = proxyServer->connect();
		proxyServer->onPacket(cb);
		registeClient(PROXY_SERVER, addr, port);
		return ok;
	}
	/**
	 * 监听本地server端口
	 * **/
	bool startServer(String addr, int port) {
		IPAddress _a;
		_a.fromString(addr);
		bool ok = proxyServer->listen(_a, port);
		return ok;
	}

	/**
	 * 连接client 因UDP是无接连特性，所以能发送成功直接保存IP
	 * **/
	bool connect(String addr, int port) {
		IPAddress _a;
		_a.fromString(addr);
		registeClient(addr + ":" + port, addr, port);
		bool ok = false;
		int t = 15;
		while (t-- >= 0) {
			ok = proxyServer->send("ping", _a, port);
		}
		return ok;
	}
	/**
	 * 注册client
	 * **/
	void registeClient(String sid, String addr, int port) {
		Serial.print("registeClient : ");
		Serial.println(sid);

		this->clients[sid] = new P2PClient(sid, addr, port);
	}
	bool sendToProxyServer(std::string message) {
		if (proxyServer == nullptr) {
			return false;
		}
		P2PClient *client = clients[PROXY_SERVER];
		return proxyServer->send(message, client->addr, client->port);
	}

	bool sendToClient(String sid, std::string message) {
		if (clients.find(sid) == clients.end()) {
			return false;
		}
		P2PClient *client = clients[sid];
		return proxyServer->send(message, client->addr, client->port);
	}

	bool sendToClient(std::string message, String addr, int port) {
		return proxyServer->send(message, addr, port);
	}

	////////////////////////////////////////

	void keepAlive() {
		for (auto &client : clients) {
			sendToClient(client.second->sid, "ping");
		}
	}
	void close() {
		for (auto &client : clients) {
			delete client.second;
		}
		clients.clear();
		close(proxyServer);
	}
};

#endif /* MODULES_CORE_P2PSERVICE_H_ */
